﻿#region

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using CuongHanh.DAL;
using CuongHanh.Models;
using CuongHanh.Models.View;

#endregion

namespace CuongHanh.Context
{
    public class Cached
    {
        public static Cached Sigleton = new Cached();

       

        public Cached()
        {
            LoadMenu();
        }

        public void InIt()
        {
        }

        #region Menu

        private readonly Dictionary<int, List<CategoryView>> _cacheMenu = new Dictionary<int, List<CategoryView>>();

        private readonly object _lockMenu = new object();

        private void LoadMenu()
        {
            //LoadMenuByBranch(AppUtil.GEM_CODE);
            LoadMenuByBranch(AppUtil.STONE_CODE);
            LoadMenuByBranch(AppUtil.WOOD_CODE);
        }

        public void LoadMenuByBranch(int id)
        {
            lock (_lockMenu)
            {
                List<CategoryView> menus = new List<CategoryView>();
                List<Category> categories = CacheContext<Category>.Singleton.GetObjectsByFilter(c => c.BranchId == id && !c.Inactive).OrderBy(c => c.No).ToList();
                List<SubCategory> subCategories = CacheContext<SubCategory>.Singleton.GetObjectsByFilter(c => c.BranchId == id && !c.Inactive).ToList();
                foreach (Category category in categories)
                {
                    CategoryView curCategory = new CategoryView(category);
                    foreach (SubCategory subCategory in subCategories.Where(c => c.CategoryId == category.Id).OrderBy(c => c.No))
                        curCategory.SubCategoriesView.Add(new SubCategoryView(subCategory));
                    menus.Add(curCategory);
                }
                _cacheMenu[id] = menus;
            }
        }

        public List<CategoryView> GetMenuByBranchId(int id)
        {
            lock (_lockMenu)
            {
                return _cacheMenu.ContainsKey(id)
                        ? _cacheMenu[id]
                        : new List<CategoryView>();
            }
        }

        #endregion

        //#region Product BestSaller

        //private readonly Dictionary<int, Dictionary<int, Product>> _bestSallers = new Dictionary<int, Dictionary<int, Product>>();

        //private readonly object _lockBestSaller = new object();

        //public void LoadBestSaller()
        //{
        //    lock (_lockBestSaller)
        //    {
        //        using (UnitOfWork unitOfWork = new UnitOfWork())
        //        {
        //            IOrderedQueryable<Product> products = unitOfWork.Where<Product>(c => c.BestSaler && !c.Invisible && !c.Inactive).OrderBy(c => c.BranchId);
        //            int branchId = 0;
        //            Dictionary<int, Product> models = new Dictionary<int, Product>();
        //            foreach (Product product in products)
        //            {
        //                if(branchId != product.BranchId)
        //                {
        //                    models = new Dictionary<int, Product>();
        //                    _bestSallers[product.BranchId] = models;
        //                    branchId = product.BranchId;
        //                }
        //                models[product.Id] = product;
        //            }
        //        }
        //    }
        //}

        //public List<ProductView> GetBestSaller(int branchId,
        //        string language)
        //{
        //    lock (_lockBestSaller)
        //    {
        //        List<ProductView> productViews = new List<ProductView>();
        //        if(_bestSallers.ContainsKey(branchId))
        //        {
        //            Dictionary<int, Product> products = _bestSallers[branchId];
        //            int i = 0;
        //            foreach (KeyValuePair<int, Product> product in products)
        //            {
        //                i++;
        //                if(i == 10)
        //                    break;
        //                productViews.Add(new ProductView(product.Value,
        //                        language));
        //            }
        //        }
        //        return productViews;
        //    }
        //}

        //public void UpdateBestSaller(Product product)
        //{
        //    lock (_lockBestSaller)
        //    {
        //        if(!_bestSallers.ContainsKey(product.BranchId))
        //            _bestSallers[product.BranchId] = new Dictionary<int, Product>();
        //        Dictionary<int, Product> products = _bestSallers[product.BranchId];
        //        if(product.Invisible
        //           || !product.BestSaler
        //           || product.Inactive)
        //        {
        //            if(products.ContainsKey(product.Id))
        //                products.Remove(product.Id);
        //        }
        //        else if(product.BestSaler
        //                && !product.Invisible
        //                && !product.Invisible)
        //            products[product.Id] = product;
        //    }
        //}

        //#endregion

        //#region Product New

        //private readonly object _lockNewProduct = new object();

        //private readonly Dictionary<int, Dictionary<int, Product>> _newProducts = new Dictionary<int, Dictionary<int, Product>>();

        //public void LoadNewProduct()
        //{
        //    lock (_lockNewProduct)
        //    {
        //        using (UnitOfWork unitOfWork = new UnitOfWork())
        //        {
        //            IOrderedQueryable<Product> products = unitOfWork.Where<Product>(c => c.New && !c.Invisible && !c.Inactive).OrderBy(c => c.BranchId);
        //            int branchId = 0;
        //            Dictionary<int, Product> models = new Dictionary<int, Product>();
        //            foreach (Product product in products)
        //            {
        //                if(branchId != product.BranchId)
        //                {
        //                    models = new Dictionary<int, Product>();
        //                    _newProducts[product.BranchId] = models;
        //                    branchId = product.BranchId;
        //                }
        //                models[product.Id] = product;
        //            }
        //        }
        //    }
        //}

        //public List<ProductView> GetNewProduct(int branchId,
        //        string language)
        //{
        //    lock (_lockNewProduct)
        //    {
        //        List<ProductView> productViews = new List<ProductView>();
        //        if(_newProducts.ContainsKey(branchId))
        //        {
        //            Dictionary<int, Product> products = _newProducts[branchId];
        //            int i = 0;
        //            foreach (KeyValuePair<int, Product> product in products)
        //            {
        //                i++;
        //                //if(i == 10)
        //                //    break;
        //                productViews.Add(new ProductView(product.Value,
        //                        language));
        //            }
        //        }
        //        return productViews;
        //    }
        //}

        //public void UpdateNewProduct(Product product)
        //{
        //    lock (_lockNewProduct)
        //    {
        //        if(!_newProducts.ContainsKey(product.BranchId))
        //            _newProducts[product.BranchId] = new Dictionary<int, Product>();
        //        Dictionary<int, Product> products = _newProducts[product.BranchId];
        //        if(product.Invisible
        //           || !product.New
        //           || product.Inactive)
        //        {
        //            if(products.ContainsKey(product.Id))
        //                products.Remove(product.Id);
        //        }
        //        else if(product.New
        //                && !product.Invisible
        //                && !product.Invisible)
        //            products[product.Id] = product;
        //    }
        //}

        //#endregion
    }

    public class CacheContext<T>
            where T : BaseClass
    {
        public static CacheContext<T> Singleton = new CacheContext<T>();

        private readonly Dictionary<int, T> _cacheDatas = new Dictionary<int, T>();

        private readonly object _lockCache = new object();

        private bool NoInactive = false;

        public CacheContext()
        {
            GetNoInactive();
            LoadCache();
        }

        private void GetNoInactive()
        {
            Type notMappedAttribute = typeof (NotMappedAttribute);
            PropertyInfo inactive = null;
            foreach (var propertyInfo in typeof(T).GetProperties())
            {
                if(propertyInfo.Name != "Inactive")
                    continue;
                inactive = propertyInfo;
                break;
            }
            if(inactive == null)
                return;
            var customAttributes = inactive.CustomAttributes.Select(c => c.AttributeType).ToList();
            foreach (Type type in customAttributes)
            {
                if(notMappedAttribute != type)
                    continue;
                NoInactive = true;
                return;
            }
        }

        public void LoadCache()
        {
            lock (_lockCache)
            {
                using (UnitOfWork unitOfWork = new UnitOfWork())
                {
                    IQueryable<T> models = NoInactive
                            ? unitOfWork.Where<T>()
                            : unitOfWork.Where<T>(c => !c.Inactive);
                    foreach (T model in models)
                        _cacheDatas[model.Id] = model;
                }
            }
        }

        public void UpdateCached(T model)
        {
            lock (_lockCache)
            {
                if(model.Id == 0)
                    return;

                if(!NoInactive
                   && model.Inactive
                   && _cacheDatas.ContainsKey(model.Id))
                    _cacheDatas.Remove(model.Id);
                else
                    _cacheDatas[model.Id] = model;
            }
        }

        /// <summary>
        ///     chi su dung cho truong hop NoInactive
        /// </summary>
        /// <param name="model"></param>
        public void DeleteCached(T model)
        {
            lock (_lockCache)
            {
                if(_cacheDatas.ContainsKey(model.Id))
                    _cacheDatas.Remove(model.Id);
            }
        }

        public IEnumerable<T> GetObjectsByFilter(Expression<Func<T, bool>> filter = null)
        {
            lock (_lockCache)
            {
                if(filter != null)
                {
                    return _cacheDatas.Values.AsQueryable().Where(filter);
                }
                return _cacheDatas.Values;
            }
        }

        public T GetObject(int id)
        {
            lock (_lockCache)
            {
                return _cacheDatas.ContainsKey(id)
                        ? _cacheDatas[id]
                        : null;
            }
        }

        public T FirstOrDefaultObject(Expression<Func<T, bool>> filter = null)
        {
            lock (_lockCache)
            {
                return filter != null
                        ? _cacheDatas.Values.AsQueryable().FirstOrDefault(filter)
                        : null;
            }
        }

        public void SetObject(int id,
                T entity)
        {
            lock (_lockCache)
            {
                _cacheDatas[id] = entity;
            }
        }

        public T1 GetValueByField<T1>(T model,
                string idField)
        {
            Type type = typeof (T);
            PropertyInfo info = type.GetProperty(idField);
            if(info != null)
                return (T1) info.GetValue(model);
            return default(T1);
        }

        public bool CompareValueByField(T model,
                string idField,
                bool value)
        {
            Type type = typeof (T);
            PropertyInfo info = type.GetProperty(idField);
            return info != null && (bool) info.GetValue(model) == value;
        }
    }
}
